package app

import (
	"gitee.com/zhucheer/orange/cfg"
	"gitee.com/zhucheer/orange/encrypt"
	"gitee.com/zhucheer/orange/internal"
	"gitee.com/zhucheer/orange/logger"
	"gitee.com/zhucheer/orange/prome"
	"gitee.com/zhucheer/orange/session"
	"time"
)

// MiddlewareFunc defines a function to process middleware.
type MiddlewareFunc func(HandlerFunc) HandlerFunc

type HandlerFunc func(*Context) error

type MiddleWare interface {
	Func() MiddlewareFunc
}

// 翻转中间件，因执行时是从最外层函数开始，定义时需要从最后一个中间件开始定义
func reverseMiddleWare(middlewares []MiddleWare) []MiddleWare {
	length := len(middlewares)
	for i := 0; i < length/2; i++ {
		middlewares[length-1-i], middlewares[i] = middlewares[i], middlewares[length-1-i]
	}
	return middlewares
}

func startCsrfToken(session session.Store) string {
	csrfToken := internal.GetAppKey() + time.Now().String()
	csrfTokenMd5 := encrypt.Md5ToUpper(csrfToken)
	sessionKey := "CSRF-TOKEN"

	if token := session.Get(sessionKey); token != nil {
		return token.(string)
	}
	session.Set(sessionKey, csrfTokenMd5)

	return csrfTokenMd5
}

func checkCsrfToken(next HandlerFunc) HandlerFunc {
	return func(c *Context) error {
		tokenInHeader := c.request.Header.Get("CSRF-TOKEN")
		if c.request.Method == "POST" && tokenInHeader != c.CsrfToken {
			return c.ResponseWrite([]byte("csrf token verify error"))
		}
		return next(c)
	}
}

// accessLog 请求日志
func accessLog(next HandlerFunc) HandlerFunc {
	return func(c *Context) error {
		appName := cfg.GetString("app.name", cfg.ConfigDef.GetString("app.name"))

		c.AddDelayAfterDo(func(c *Context) {
			if cfg.GetBool("prome.open", false) {
				promeHandler := prome.PromeHandler()
				promeHandler.ReqCnt.WithLabelValues(c.Request().Method, c.RoutePath()).Inc()
				promeHandler.DurSummary.WithLabelValues(c.Request().Method, c.RoutePath()).Observe(float64(c.GetMs().Milliseconds()))

			}

			// Ignore accessLog path
			ignorePathList := cfg.GetSliceString("app.accessLogIgnore", []string{})
			if cfg.GetBool("app.accessLog", cfg.ConfigDef.GetBool("app.accessLog")) &&
				!inSlice(c.RoutePath(), ignorePathList) {

				reqBody := getSplitString(string(c.OrangeInput.FormBody), 500)
				responseBody := getSplitString(string(c.ResponseBody()), 500)

				logger.Infow("ACCESS-LOG",
					"appName", appName,
					"method", c.request.Method,
					"routePath", c.RoutePath(),
					"host", c.OrangeInput.Host(),
					"uri", c.OrangeInput.URI(),
					"costMilli", c.GetMs().Milliseconds(),
					"requestRaw", reqBody,
					"response", responseBody)
			}
		}, 0)
		return next(c)
	}
}

func getSplitString(val string, maxLen int) (resp string) {
	if len(val) > maxLen {
		resp = val[:maxLen] + "...EOF"
	} else {
		resp = val
	}
	return resp
}

func inSlice(needle string, haystack []string) bool {
	result := false
	for _, item := range haystack {
		if needle == item {
			result = true
			break
		}
	}
	return result
}
